package com.mp.approvalreq.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import com.mp.approvalreq.dao.*;
import com.mp.approvalreq.dao.impl.*;
import com.mp.approvalreq.entity.*;
import com.mp.approvalreq.entity.common.Constants;
import com.mp.approvalreq.entity.po.GlobalSetting;
import com.mp.approvalreq.entity.vo.Component;
import com.mp.approvalreq.exception.SpecialLogicException;
import com.mp.approvalreq.service.ApprovalOperateServicePrx;
import com.mp.approvalreq.service.SubmitRequestServiceServant;
import com.mp.approvalreq.service.TemplateServiceServant;
import com.mp.approvalreq.service.strategy.DuplicateStrategyImpl;
import com.mp.approvalreq.service.strategy.FlowFactory;
import com.mp.approvalreq.util.*;
import com.mp.checkin.entity.LeaveInfo;
import com.mp.checkin.entity.TimeDetail;
import com.mp.checkin.service.CheckInDataServicePrx;
import com.mp.checkin.util.DateTimeUtils;
import com.mp.user.entity.User;
import com.mp.user.entity.dto.AttachmentDTO;
import com.mp.user.service.AttachmentServicePrx;
import com.mp.user.service.UserServicePrx;
import com.mp.user.util.UserUtils;
import com.plusmoney.exception.ExceptionMessage;
import com.plusmoney.exception.RpcServerLogicalException;
import com.plusmoney.exception.enums.ApprovalRespCodeDesc;
import com.plusmoney.jdbc.util.DBUtils;
import com.plusmoney.redis.RedisUtils;
import com.plusmoney.util.DateTimeHelper;
import com.plusmoney.util.Utils;
import com.qq.tars.client.util.ServantUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;


public class SubmitRequestServantImpl implements SubmitRequestServiceServant {

    private static final Logger logger = LoggerFactory.getLogger(SubmitRequestServantImpl.class);

    @Override
    public Request submitRequest(Request request) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            UserServicePrx userServicePrx = ServantUtils.getObject(UserServicePrx.class);
            CcDao ccDao = DaoFactory.getCcDao(connection);
            TemplateDao templateDao = new TemplateDaoImpl(connection);
            SubmitRequestDao submitRequestDao = new SubmitRequestDaoImpl(connection);
            GlobalSettingDao globalSettingDao = new GlobalSettingDaoImpl(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);
            SelectTemplateDao selectTemplateDao = DaoFactory.getSelectTemplateDao(connection);
            LeaveDao leaveDao = new LeaveDaoImpl(connection);
            OutdoorDao outdoorDao = new OutdoorDaoImpl(connection);
            AppendSignDao appendSignDao = new AppendSignDaoImpl(connection);
            OvertimeDao overtimeDao = new OvertimeDaoImpl(connection);
            FlowDao flowDao = new FlowDaoImpl(connection);
            ParticipantDao participantDao = new ParticipantDaoImpl(connection);
            ComponentDao componentDao = new ComponentImpl(connection);
            ExpenseDao expenseDao = new ExpenseDaoImpl(connection);
            MoneyDao moneyDao = new MoneyDaoImpl(connection);

            User user = request.getUser();

            isSubmitAgain(request, user, submitRequestDao);

            //获得模板详情
            Template template = templateDao.selectTemplateDetailById(request.getTemplateId());
            logger.info("template:{}", JSONObject.toJSONString(template));
            if (Objects.equals(template.getIsFinancial(), 1) && StringUtils.isEmpty(template.getFinancialFlow())) {
                GlobalSetting globalSetting = globalSettingDao.selectGlobalSetting(user.getCompanyId());
                template.setFinancialFlow(globalSetting.getFinancialFlow());
                if (StringUtils.isEmpty(template.getFinancialFlow())) {
                    throw new RpcServerLogicalException(1, "请设置财务审批流");
                }
            }

            // 初始化并校验组件信息
            initAndCheckComponentData(request);

            // 校验
            check(request, user, template, submitRequestDao, selectTemplateDao, appendSignDao);

            // 代提交审批
            if (request.getApplicant() > 0) {
                User applicantUser = userServicePrx.getUserByContactId(request.getApplicant());
                if (!UserUtils.isSameUser(applicantUser, user)) {
                    checkProxy(applicantUser, user, template);
                    request.setProxyContactId(user.getContactId());
                    user = applicantUser;
                }
            }
            initRequest(request, template, user);

            // 处理审批流
            constructFinalRequestFlow(user, request, template, templateDao, globalSettingDao);

            Map<Integer, User> requestUserMap = RequestUtils.getRequestUserMap(request, template);

            //todo 1
            // 合并模版抄送人和临时抄送人
            mergeCcList(request, user, template, requestUserMap);

            // 处理套件
            dealSuite(template, request, user);

            // 处理参与人
            dealParticipants(request, user, requestUserMap);

            // 设置组件
            setComponentInfo(request, user, template);

            try {
                dealMoney(request, user, template);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                EmailUtils.sendMail("组装金额信息出错", e);
            }

            // 提交数据
            DBUtils.begin(connection);
            submitDataToDataBase(user, request, requestDao, submitRequestDao, ccDao, participantDao,
                    leaveDao, outdoorDao, appendSignDao, overtimeDao, expenseDao, moneyDao, flowDao, componentDao);
            DBUtils.commit(connection);

            // 提交后的处理：1.提交审批记录;  2.判断第一个审批人是否是自己,是则直接同意; 3.推送给下一个审批人;
            afterSubmitHandle(template, request, user);

            Request result = new Request();
            result.setRequestDkey(request.getRequestDkey());
            result.setRequestAncestorId(request.getRequestAncestorId());
            return result;
        } catch (Throwable e) {
            DBUtils.rollback(connection);
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private void dealMoney(Request request, User user, Template template) {
        if (request.getDefaultType() != Template.OTHER) {
            return;
        }
        List<Money> list = new ArrayList<>();
        List<TemplateComponent> sums = ComponentUtils.getComponentByType(request.getComponentsContent(), ContentType.TOTAL_MONEY);
        List<TemplateComponent> moneys = ComponentUtils.getComponentByType(request.getComponentsContent(), ContentType.MONEY);
        List<TemplateComponent> groups = ComponentUtils.getComponentByType(request.getComponentsContent(), ContentType.COMPONENT_GROUP);

        if (Utils.isNotEmpty(sums)) {
            logger.info("有金额汇总组件");
            for (TemplateComponent component : sums) {
                if (StringUtils.isEmpty(component.getValue())) {
                    continue;
                }
                BigDecimal amount = new BigDecimal(component.getValue());
                if (BigDecimal.ZERO.compareTo(amount) == 0) {
                    continue;
                }
                Money money = new Money();
                money.setCompanyId(user.getCompanyId());
                money.setCompanyInfoId(user.getCompanyInfoId());
                money.setRequestId(request.getRequestDkey());
                money.setTemplateName(template.getTemplateName());
                money.setNum(component.getNum());
                money.setName(component.getName());
                money.setAmount(amount);
                money.setIsSum(1);
                money.setApprovalNum(request.getApprovalNum());
                if (Objects.equals(String.valueOf(component.getNum()), template.getRelateAnalysisNum())) {
                    money.setIsRelatedToAmoeba(1);
                    money.setIsRelatedToCost(1);
                } else {
                    money.setIsRelatedToAmoeba(0);
                    money.setIsRelatedToCost(0);
                }
                list.add(money);
            }
            if (Utils.isNotEmpty(list)) {
                request.setMoneys(list);
            }
            return;
        }

        if (Utils.isNotEmpty(moneys)) {
            for (TemplateComponent component : moneys) {
                if (StringUtils.isEmpty(component.getValue())) {
                    continue;
                }
                BigDecimal amount = new BigDecimal(component.getValue());
                if (BigDecimal.ZERO.compareTo(amount) == 0) {
                    continue;
                }
                Money money = new Money();
                money.setApprovalNum(request.getApprovalNum());
                money.setCompanyId(user.getCompanyId());
                money.setCompanyInfoId(user.getCompanyInfoId());
                money.setRequestId(request.getRequestDkey());
                money.setTemplateName(template.getTemplateName());
                money.setNum(component.getNum());
                money.setName(component.getName());
                money.setAmount(amount);
                money.setIsSum(0);
                if (Objects.equals(String.valueOf(component.getNum()), template.getRelateAnalysisNum())) {
                    money.setIsRelatedToAmoeba(1);
                    money.setIsRelatedToCost(1);
                } else {
                    money.setIsRelatedToAmoeba(0);
                    money.setIsRelatedToCost(0);
                }
                list.add(money);
            }
        }
        if (Utils.isNotEmpty(groups)) {
            for (TemplateComponent component : groups) {
                List<GroupContent> groupContents = component.getGroupContents();
                for (GroupContent groupContent : groupContents) {
                    List<Component> subComponents = groupContent.getValue();
                    for (Component subComponent : subComponents) {
                        if (subComponent.getType() != ContentType.MONEY) {
                            continue;
                        }
                        if (StringUtils.isEmpty(subComponent.getValue())) {
                            continue;
                        }
                        BigDecimal amount = new BigDecimal(subComponent.getValue());
                        if (BigDecimal.ZERO.compareTo(amount) == 0) {
                            continue;
                        }
                        Money money = new Money();
                        money.setApprovalNum(request.getApprovalNum());
                        money.setCompanyId(user.getCompanyId());
                        money.setCompanyInfoId(user.getCompanyInfoId());
                        money.setRequestId(request.getRequestDkey());
                        money.setTemplateName(template.getTemplateName());
                        money.setNum(component.getNum());
                        money.setName(component.getName());
                        money.setSubNum(subComponent.getNum());
                        money.setSubName(subComponent.getName());
                        money.setAmount(amount);
                        money.setIsSum(0);
                        if (Objects.equals(String.valueOf(component.getNum()), template.getRelateAnalysisNum())) {
                            money.setIsRelatedToAmoeba(1);
                            money.setIsRelatedToCost(1);
                        } else {
                            money.setIsRelatedToAmoeba(0);
                            money.setIsRelatedToCost(0);
                        }
                        list.add(money);
                    }
                }
            }
        }
        logger.info("moneys:{}", JSONObject.toJSONString(list));
        if (Utils.isNotEmpty(list)) {
            request.setMoneys(list);
        }
    }

    private void initRequest(Request request, Template template, User user) {
        request.setCompanyId(user.getCompanyId());
        request.setTemplateName(template.getTemplateName());
        request.setVersion(template.getVersion());
        request.setDefaultType(template.getDefaultType());
    }

    private void checkProxy(User applicantUser, User user, Template template) {
        if (StringUtils.isEmpty(template.getProxyContactIds())) {
            throw new RpcServerLogicalException(1, "当前模版不支持代提交审批");
        }
        List<Integer> proxyContactIds = Utils.string2list(template.getProxyContactIds());
        if (!proxyContactIds.contains(user.getContactId()) || !Objects.equals(applicantUser.getCompanyId(), user.getCompanyId())) {
            throw new RpcServerLogicalException(1, "非法操作");
        }
    }

    private void setComponentInfo(Request request, User user, Template template) {
        request.setContentIds(getContentIds(request.getComponentsContent()));
        request.setContentNums(getContentNumList(request.getComponentsContent()));
        for (TemplateComponent component : request.getComponentsContent()) {
            component.setRequestId(request.getRequestDkey());
            component.setTemplateId(template.getTemplateId());
            component.setCompanyId(user.getCompanyId());
        }
    }

    /**
     * 合并模版抄送人 和 临时抄送人
     *
     * @param request
     * @param user
     * @param template
     * @param requestUserMap
     * @throws Throwable
     */
    private void mergeCcList(Request request, User user, Template template, Map<Integer, User> requestUserMap) throws Throwable {

        UserServicePrx userServicePrx = ServantUtils.getObject(UserServicePrx.class);
        Map<Integer, User> userMap;
        if (Utils.isEmpty(requestUserMap)) {
            List<User> userList = userServicePrx.getAllUserBaseInfoList(user.getCompanyId());
            userMap = ApprovalUtils.getUserMap(userList);
        } else {
            userMap = requestUserMap;
        }

        List<Cc> ccList = new ArrayList<>();
        if (StringUtils.isNotBlank(template.getCc())) {
            logger.info("模版抄送人:{}", template.getCc());
            if (ApprovalUtils.isTestCompany(user.getCompanyId())) {
                List<String> templateCcList = ListHelper.string2StringList(template.getCc());
                if (templateCcList.contains("SP") || templateCcList.contains("DM") || templateCcList.contains("DSP") || templateCcList.contains("DDM")) {
                    List<String> ids = new ArrayList<>();
                    Map<String, List<User>> userSuperiorMap = CcUtils.get4UserSuperior(user);
                    logger.info("userSuperiorMap:{}",JSONObject.toJSONString(userSuperiorMap));
                    for (List<User> users : userSuperiorMap.values()) {
                        for (User u : users) {
                            requestUserMap.put(u.getContactId(), u);
                        }
                    }
                    for (String key : templateCcList) {
                        if (ValidateUtil.isPositiveInteger(key)) {
                            ids.add(key);
                            continue;
                        }
                        List<String> list = findIdsByKey(userSuperiorMap, key);
                        if (Utils.isNotEmpty(list)) {
                            ids.addAll(list);
                        }
                    }
                    template.setCc(ListHelper.list2string(ids));
                }
            }

            List<Integer> templateCc = Utils.string2list(template.getCc());
            for (Integer ccId : templateCc) {
                User ccUser = userMap.get(ccId);
                if (Utils.isNull(ccUser)) {
                    logger.error("抄送人不存在:" + ccId);
                    ccUser = userServicePrx.getUserByContactId(ccId);
                    if (Objects.equals(ccUser.getQuit(), true)) {
                        TemplateServiceServant templateServiceServant = new TemplateServantImpl();
                        templateServiceServant.handleQuitTemplate(ccUser);
                        logger.info("已剔除抄送人");
                        continue;
                    }
                }
                Cc cc = new Cc();
                cc.setRequestId(request.getRequestDkey());
                cc.setCcCiid(ccUser.getCompanyInfoId());
                cc.setCcId(ccId);
                cc.setIsTemp(0);
                cc.setCompanyId(user.getCompanyId());
                if (ccList.contains(cc)) {
                    logger.info("抄送人已存在:{}", cc.getCcId());
                    continue;
                }
                ccList.add(cc);
            }
        }
        if (Utils.isNotEmpty(request.getCcList())) {
            for (Cc cc : request.getCcList()) {
                if (!userMap.containsKey(cc.getCcId())) {
                    logger.error("抄送人不存在:" + cc.getCcId());
                    continue;
                }
                cc.setRequestId(request.getRequestDkey());
                cc.setCcCiid(userMap.get(cc.getCcId()).getCompanyInfoId());
                if (ccList.contains(cc)) {
                    logger.info("{}为模版抄送人", cc.getCcId());
                    continue;
                }
                cc.setCompanyId(user.getCompanyId());
                cc.setCreatorId(user.getCompanyInfoId());
                cc.setIsTemp(1);
                ccList.add(cc);
            }
        }
        logger.info("ccList:{}", ccList);
        request.setCc(CcUtils.getCc(ccList));
        request.setCcCiid(CcUtils.getCcCiid(ccList));
        request.setCcList(ccList);
    }

    private List<String> findIdsByKey(Map<String, List<User>> userSuperiorMap, String key) {
        List<User> users = userSuperiorMap.get(key);
        if (Utils.isEmpty(users)) {
            return new ArrayList<>();
        }
        return users.stream().map(u -> u.getContactId().toString()).collect(Collectors.toList());
    }

    private void dealParticipants(Request request, User user, Map<Integer, User> userMap) {
        if (StringUtils.isNotBlank(request.getRelaPeople())) {
            List<Integer> contactIds = Utils.string2list(request.getRelaPeople());
            List<Participant> participants = new ArrayList<>();
            for (Integer contactId : contactIds) {
                User u = userMap.get(contactId);
                if (Utils.isNull(u)) {
                    continue;
                }
                Participant participant = new Participant();
                participant.setCompanyId(user.getCompanyId());
                participant.setRequestId(request.getRequestDkey());
                participant.setContactId(u.getContactId());
                participant.setCompanyInfoId(u.getCompanyInfoId());
                participants.add(participant);
            }
            request.setRelaPeople(ParticipateUtils.getContactIds(participants));
            request.setRelaPeopleCiid(ParticipateUtils.getCompanyInfoIds(participants));
            request.setParticipants(participants);
        }
    }

    @Override
    public void editRequestFlows(Request dto) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            FlowDao flowDao = new FlowDaoImpl(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);
            TemplateDao templateDao = new TemplateDaoImpl(connection);
            User user = dto.getUser();

            Request request = requestDao.selectRequestDetailById(dto.getRequestDkey());
            Template template = templateDao.selectTemplateByRequestId(dto.getRequestDkey());

            checkEditFlows(request, template, dto.getFlows());

            List<Flow> newFlows;
            List<Flow> oldFlows = flowDao.selectFlowsByRequestId(dto.getRequestDkey());
            List<Flow> financialFlows = FlowUtils.getFinancialFlows(oldFlows);
            List<Flow> defaultFlows = FlowUtils.getDefaultFlows(oldFlows);
            if (Utils.isNotEmpty(dto.getFlows())) {
                TreeMap<Integer, List<Flow>> flowMap = FlowUtils.groupByOrdering(dto.getFlows());
                if (flowMap.lastKey() != flowMap.size()) {
                    EmailUtils.sendMail("提交审批流数据错误", dto);
                    for (int i = 1; i <= flowMap.lastKey(); i++) {
                        if (flowMap.containsKey(i)) {
                            continue;
                        }
                        for (int j = i + 1; i <= flowMap.lastKey(); j++) {
                            if (!flowMap.containsKey(j)) {
                                continue;
                            }
                            List<Flow> flows = flowMap.get(j);
                            for (Flow flow : flows) {
                                flow.setOrdering(flow.getOrdering() - 1);
                            }
                        }
                        break;
                    }
                    EmailUtils.sendMail("修改后的数据", dto);
                }

                Map<Flow, Flow> map = new HashMap<>();
                newFlows = new ArrayList<>();
                for (Flow oldFlow : defaultFlows) {
                    if (oldFlow.getIsApproved() > 0 || oldFlow.getIsApproved() == Flow.SKIP) {
                        if (!dto.getFlows().contains(oldFlow)) {
                            throw new RpcServerLogicalException(1, "已审批的审批流不能改变");
                        }
                        map.put(oldFlow, oldFlow);
                    }
                }
                for (Flow flow : dto.getFlows()) {
                    newFlows.add(map.getOrDefault(flow, flow));
                }
            } else {
                String newJudgerIdStr = dto.getAllJudgerToEdit();
                if (StringUtils.isBlank(newJudgerIdStr)) {
                    throw new RpcServerLogicalException(1, "请选择审批流");
                }

                UserServicePrx userServicePrx = ServantUtils.getObject(UserServicePrx.class);
                List<User> userList = userServicePrx.getUserListByContactIds(newJudgerIdStr);
                List<Integer> newJudgerIds = Utils.string2list(newJudgerIdStr);
                Map<Integer, Integer> idMap = userList.stream().collect(Collectors.toMap(User::getContactId, User::getCompanyInfoId, (a, b) -> b));
                List<Integer> newJudgerCompanyInfoIds = newJudgerIds.stream().map(idMap::get).collect(Collectors.toList());
                logger.info("新审批流:{}", newJudgerCompanyInfoIds);
                if (StringUtils.isNotBlank(request.getWhoConfirmCiid())) {
                    List<Integer> confirmCompanyInfoIds = Utils.string2list(request.getWhoConfirmCiid());
                    logger.info("已审批:{}", confirmCompanyInfoIds);
                    if (Utils.isNotEmpty(confirmCompanyInfoIds)) {
                        for (int i = 0; i < confirmCompanyInfoIds.size(); i++) {
                            if (!Objects.equals(confirmCompanyInfoIds.get(i), newJudgerCompanyInfoIds.get(i))) {
                                throw new RpcServerLogicalException(1, "已审批的审批流不能改变");
                            }
                        }
                    }
                }
                newFlows = getNewFlows(request, user, newJudgerIds, newJudgerCompanyInfoIds, defaultFlows);
            }

            FlowUtils.resetFlows(newFlows);
            if (Utils.isNotEmpty(financialFlows)) {
                int maxOrdering = FlowUtils.getMaxOrdering(newFlows);
                for (Flow financialFlow : financialFlows) {
                    financialFlow.setOrdering(++maxOrdering);
                }
                newFlows.addAll(financialFlows);
            }
            logger.info("最终审批流:{}", JSONObject.toJSONString(newFlows));

            FlowUtils.transferRequestInfo(request, user, newFlows);

            DBUtils.begin(connection);
            flowDao.deleteBatchFlows(oldFlows);
            flowDao.insertFlows(newFlows);
            requestDao.updateRequestInfo(request);
            DBUtils.commit(connection);
        } catch (Throwable e) {
            DBUtils.rollback(connection);
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e, logger);
        } finally {
            DBUtils.release(connection);
        }
    }

    private void checkEditFlows(Request request, Template template, List<Flow> flows) {
        if (Utils.isNull(request)) {
            throw new RpcServerLogicalException(1, "审批不存在");
        }
        if (Utils.isNull(template)) {
            throw new RpcServerLogicalException(1, "模板不存在");
        }
        if (request.getFinished() != Finished.APPROVING) {
            throw new RpcServerLogicalException(1, "只有审批中的审批才能更改审批流");
        }
        if (Utils.isNotEmpty(flows)) {
            for (Flow flow : flows) {
                if (flow.getContactId() <= 0) {
                    throw new RpcServerLogicalException(1, "contactId不能为空");
                }
                if (flow.getCompanyInfoId() <= 0) {
                    throw new RpcServerLogicalException(1, "companyInfoId不能为空");
                }
                flow.setCompanyId(request.getCompanyId());
                flow.setRequestId(request.getRequestDkey());
            }
        }
    }

    /**
     * 组装新审批流
     *
     * @param request
     * @param user
     * @param newJudgerIds
     * @param newJudgerCompanyInfoIds
     * @param oldFlows
     * @return
     */
    private List<Flow> getNewFlows(Request request, User user, List<Integer> newJudgerIds, List<Integer> newJudgerCompanyInfoIds, List<Flow> oldFlows) {
        List<Flow> newFlows = new ArrayList<>();
        for (int i = 0; i < newJudgerCompanyInfoIds.size(); i++) {
            Integer companyInfoId = newJudgerCompanyInfoIds.get(i);
            if (oldFlows.size() > i) {
                Flow flow = oldFlows.get(i);
                if (StringUtils.isNotBlank(flow.getIsApprovedAt())) {
                    // 已经批准
                    newFlows.add(flow);
                    continue;
                }
                if (Objects.equals(flow.getCompanyInfoId(), companyInfoId)) {
                    newFlows.add(flow);
                    continue;
                }
            }
            Flow newFlow = new Flow();
            newFlow.setCompanyId(user.getCompanyId());
            newFlow.setContactId(newJudgerIds.get(i));
            newFlow.setCompanyInfoId(companyInfoId);
            newFlow.setRequestId(request.getRequestDkey());
            newFlow.setOrdering(i + 1);
            if (i > request.getRequestStep()) {
                newFlow.setIsApproved(Flow.NOT_YET);
            }
            newFlows.add(newFlow);
        }
        return newFlows;
    }


    @Override
    public String getApprovalFlow(User user, Request request) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            TemplateDao templateDao = new TemplateDaoImpl(connection);
            GlobalSettingDao globalSettingDao = new GlobalSettingDaoImpl(connection);
            Template template = templateDao.getTemplate(request.getTemplateId(), user.getCompanyId());

            if (RequestFlowType.ALTERNATIVE.equals(template.getRequestFlow())) {
                logger.error("无法显示灵活审批人");
                return "0";
            }

            if (RequestFlowType.CONDITION.equals(template.getRequestFlow())) {
                List<ConditionFlow> conditionFlows = templateDao.selectConditionFlowList(template.getTemplateId());
                if (Utils.isEmpty(conditionFlows)) {
                    logger.error("模板数据错误，条件设置不存在");
                    return "0";
                }

                if (Utils.isEmpty(request.getComponentsContent())) {
                    logger.info("条件审批关联组参数不能为空");
                    return "0";
                }
                Map<Integer, TemplateComponent> componentMap = ComponentUtils.getComponentMap(request.getComponentsContent());
                String templateFlow = ConditionFlowUtils.chooseConditionFlow(request, conditionFlows, componentMap, template);
                logger.info("条件审批流:{}", templateFlow);
                template.setRequestFlow(templateFlow);
            }

            GlobalSetting globalSetting = globalSettingDao.selectGlobalSetting(user.getCompanyId());
            DuplicateStrategy duplicateStrategy = new DuplicateStrategyImpl(globalSetting.getDuplicateStrategy());
            List<Flow> flows = FlowFactory.build(user, template.getRequestFlow(), request.getAllJudgerToEdit());

            return FlowUtils.getContactIds(duplicateStrategy.removeRepeat(flows));
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            return "0";
        } finally {
            DBUtils.release(connection);
        }
    }

    @Override
    public List<Flow> getRequestFlows(Request dto) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            TemplateDao templateDao = new TemplateDaoImpl(connection);
            GlobalSettingDao globalSettingDao = new GlobalSettingDaoImpl(connection);

            User user = dto.getUser();
            Template template = templateDao.selectTemplateDetailById(dto.getTemplateId());

            if (RequestFlowType.ALTERNATIVE.equals(template.getRequestFlow())) {
                logger.error("无法显示灵活审批人");
                return null;
            }

            if (RequestFlowType.CONDITION.equals(template.getRequestFlow())) {
                List<ConditionFlow> conditionFlows = templateDao.selectConditionFlowList(template.getTemplateId());
                if (Utils.isEmpty(conditionFlows)) {
                    logger.error("模板数据错误，条件设置不存在");
                    return null;
                }

                if (Utils.isEmpty(dto.getComponentsContent())) {
                    logger.info("条件审批关联组参数不能为空");
                    return null;
                }
                Map<Integer, TemplateComponent> componentMap = ComponentUtils.getComponentMap(dto.getComponentsContent());
                String templateFlow = ConditionFlowUtils.chooseConditionFlow(dto, conditionFlows, componentMap, template);
                logger.info("条件审批流:{}", templateFlow);
                template.setRequestFlow(templateFlow);
            }

            GlobalSetting globalSetting = globalSettingDao.selectGlobalSetting(user.getCompanyId());
            DuplicateStrategy duplicateStrategy = new DuplicateStrategyImpl(globalSetting.getDuplicateStrategy());


            List<Flow> flows = FlowFactory.build(user, template.getRequestFlow(), dto.getAllJudgerToEdit());
            flows = duplicateStrategy.removeRepeat(flows);

            if (Objects.equals(template.getIsFinancial(), 1)) {
                String financialFlow = StringUtils.isNotEmpty(template.getFinancialFlow()) ? template.getFinancialFlow() : globalSetting.getFinancialFlow();
                if (StringUtils.isEmpty(financialFlow)) {
                    throw new RpcServerLogicalException(1, "付款审批流不能为空");
                }
                List<Flow> financialFlows = FlowFactory.build(user, financialFlow, null);
                int maxOrdering = FlowUtils.getMaxOrdering(flows);
                for (Flow ff : financialFlows) {
                    // 追加到普通审批流后面
                    ff.setOrdering(++maxOrdering);
                    ff.setFlowType(Flow.FLOW_TYPE_FINANCIAL);
                    ff.setCompanyId(user.getCompanyId());
                }
                flows = ListUtils.union(flows, financialFlows);
            }
            return flows;
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            return null;
        } finally {
            DBUtils.release(connection);
        }
    }

    private void checkDeptRangePermission(Template template, User user) throws Throwable {
        if (user.getIsAdmin() == 1) {
            return;
        }

        if (template.getCompanyId() != 0 && !Objects.equals(template.getCompanyId(), user.getCompanyId())) {
            throw new RpcServerLogicalException(ApprovalRespCodeDesc.ILLEGAL_OPERATION);
        }

        // dept为0表示全公司可用, 有限制 则到具体的人要看rangespecific
        String limitDepts = template.getDepartmentId();
        if (Objects.equals(limitDepts, "0")) {
            return;
        }

        List<String> deptIds = ListHelper.string2NumStringList(user.getDeptPath());
        for (String limitDeptId : limitDepts.split(",")) {
            for (String deptId : deptIds) {
                if (Objects.equals(limitDeptId, deptId)) {
                    return;
                }
            }
        }

        if (StringUtils.isNotEmpty(template.getRangeSpecific())) {
            List<String> rangeSpecifics = ListHelper.string2NumStringList(template.getRangeSpecific());
            if (rangeSpecifics.contains(user.getContactId().toString())) {
                return;
            }
        }

        // 标记
        if (ApprovalUtils.isMoreTemplateType(template.getDefaultType())) {
            // 所有身份的deptIds,boss不能调用该方法
            List<String> allMultiUserDeptIds = getAllMultiUserDeptIds(user.getCompanyId(), user.getCompanyInfoId());

            if (ApprovalUtils.isSpecifiedDepts(template.getDepartmentId())) {
                if (ListHelper.hasEqualsElement(allMultiUserDeptIds, deptIds)) {
                    return;
                }
            }

            if (StringUtils.isNotEmpty((template.getCompanyInfoIdRange()))) {
                List<Integer> companyInfoIds = ListHelper.string2IntegerList(template.getCompanyInfoIdRange());
                if (companyInfoIds.contains(user.getCompanyInfoId())) {
                    return;
                }
            }
        }

        throw new RpcServerLogicalException(1, "您不在审批适用范围内,无法提交");
    }


    private List<String> getAllMultiUserDeptIds(Integer companyId, Integer companyInfoId) throws Throwable {
        UserServicePrx userService = ServantUtils.getObject(UserServicePrx.class);
        List<User> users = userService.getMultiUserListByCompanyInfoId(companyId, companyInfoId);

        List<String> deptPath = new ArrayList<>();
        for (User user : users) {
            deptPath.addAll(ListHelper.string2StringList(user.getDeptPath()));
        }

        return deptPath;
    }


    private void checkNeedFilling(Request request, Template template) {
        if (Objects.equals(template.getIsAllowedToEditJudger(), 1)
                || RequestFlowType.ALTERNATIVE.equals(request.getRequestFlow())) {
            if (StringUtils.isEmpty(request.getAllJudgerToEdit()) && Utils.isEmpty(request.getFlows())) {
                throw new RpcServerLogicalException(1, "请选择第一层审批人");
            }
        }

        // 属于下列版本的模板不在这校验
        if (notInHereCheck(request, template)) {
            return;
        }
        // 强制关联
        int mustRelate = 0;
        List<TemplateComponent> templateComponents = JSONArray.parseArray(template.getTemplateComponent(), TemplateComponent.class);

        logger.info("数据库模板组件: " + JSON.toJSONString(templateComponents));
        logger.info("前端参数组件: " + JSON.toJSONString(request.getComponentsContent()));

        for (TemplateComponent templateComponent : templateComponents) {
            // 老版本补打卡 考勤方案不校验
            if (templateComponent.getType() == ContentType.ATTENDANCE_PLAN) {
                continue;
            }
            if (templateComponent.getNeed() == 1) {
                boolean pass = false;
                for (TemplateComponent component : request.getComponentsContent()) {
                    if (Objects.equals(component.getNum(), templateComponent.getNum())) {
                        if (StringUtils.isEmpty(component.getValue())) {
                            throw new RpcServerLogicalException(1, "请填写必填项: " + templateComponent.getName());
                        }
                        if (templateComponent.getType() == ContentType.MONEY) {
                            if (!ValidateUtil.isMoney(component.getValue())) {
                                throw new RpcServerLogicalException(1, templateComponent.getName() + " 仅支持输入数字");
                            }
                        }
                        pass = true;
                    }
                    if (component.getType() == ContentType.RELATED_CLIENT || component.getType() == ContentType.RELATED_TASK) {
                        mustRelate = 1;
                    }
                }
                if (!pass) {
                    throw new RpcServerLogicalException(1, "请填写必填项: " + templateComponent.getName());
                }
            }
        }

        // 如果强制关联了客户或任务
        if (Objects.equals(template.getMustRelate(), 1)) {
            if (mustRelate == 0) {
                throw new RpcServerLogicalException(1, "必须关联任务或客户");
            }
        }
    }

    private boolean notInHereCheck(Request request, Template template) {
        try {
            //  新版报销模板单独校验(此时generation=1,提交时都是1,再之前没有这个字段,默认会是0)
            if (template.getDefaultType() == Template.EXPENSES
                    && StringUtils.isNotEmpty(request.getTotalMoney())) {
                return true;
            }

            //   新版补打卡模板单独校验(此时generation=2,提交时都是2,即使是新版补打卡也会是2了)
            if (template.getDefaultType() == Template.APPEND_SIGN && request.getGeneration() >= 2) {
                return true;
            }
        } catch (Exception e) {
            return false;
        }

        // 加班模板单独校验
        return template.getDefaultType() == Template.WORK_OVERTIME;
    }

    private void checkAmountTop(Request request, Template template) {
        if (Utils.isNull(template.getAmountTop())) {
            return;
        }
        if (template.getAmountTop().compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal amountTop = template.getAmountTop();
            logger.info("模板设置报销上限为 " + amountTop);

            boolean isNewTemplate = TemplateUtils.isNewExpenseTemplate(template.getDefaultType(), request);

            if (isNewTemplate) {
                BigDecimal totalMoney = new BigDecimal(request.getTotalMoney());  // 新版本的报销模板
                if (totalMoney.compareTo(amountTop) > 0) {
                    throw new RpcServerLogicalException(1, "报销总额大于上限");
                }

            } else {        // 老的报销模板或者申购模板

                List<TemplateComponent> templateComponents = request.getComponentsContent();
                for (TemplateComponent component : templateComponents) {
                    if (component.getNum() == 2 && component.getType() == ContentType.MONEY) {
                        BigDecimal payMoney = new BigDecimal(component.getValue());
                        if (payMoney.compareTo(amountTop) > 0) {
                            logger.info("审批报销为 " + payMoney);
                            throw new RpcServerLogicalException(1, "报销金额大于上限");
                        }
                    }
                }
            }
        }
    }

    private void checkAndDecodeAttach(Request request, Template template) {
        List<TemplateComponent> needList = JSONArray.parseArray(template.getTemplateComponent(), TemplateComponent.class);
        List<TemplateComponent> componentList = request.getComponentsContent();
        for (TemplateComponent templateComponent : needList) {
            for (int j = 0; j < componentList.size(); j++) {
                TemplateComponent component = componentList.get(j);
                if (Objects.equals(component.getNum(), templateComponent.getNum())) {
                    component.setType(templateComponent.getType());
                    if (templateComponent.getType() == ContentType.ATTACHMENT) {
                        if (StringUtils.isNotEmpty(component.getValue())) {
                            if (Objects.equals(component.getValue(), "-1")) {
                                EmailUtils.sendErrorMail("附件上传出错", JSONObject.toJSONString(request));
                                throw new RpcServerLogicalException(1, "附件上传失败,请重新上传");
                            }
                            List<String> list = JSONArray.parseArray(component.getValue(), String.class);
                            for (String value : list) {
                                if (Utils.isNull(value) || "null".equals(value.toLowerCase())) {
                                    EmailUtils.sendErrorMail("附件上传出错", JSONObject.toJSONString(request));
                                    throw new RpcServerLogicalException(1, "附件上传失败,请重新上传");
                                }
                            }
                        }
                    }
                    componentList.set(j, component);
                }
            }
        }
        request.setComponentsContent(componentList);
    }

    /**
     * 处理审批拒绝链
     *
     * @param request
     * @throws Throwable
     */
    private Request dealWithRequestChain(Request request, User user, SubmitRequestDao submitRequestDao) throws Throwable {
        // 看请求里有没有parentRequestId，有的话说明是基于某个来编辑的，查出链表，找出之前的链，加入这个链，让以前的请求不可见(Fis_resubmit = 1)
        int parentRequestId = Utils.isNull(request.getParentRequestId()) ? 0 : request.getParentRequestId();
        if (parentRequestId != 0) {

            //  这个方法会获得审批的祖先id，会传承下来
            request = submitRequestDao.getRequestRefuseChain(request);
            isTwiceSubmitParentRequest(request);

            submitRequestDao.updateRequestResubmit(parentRequestId, user.getCompanyId());
            RocketUtils.clearRequestRedPoint(parentRequestId, user.getCompanyId());

            // 如果上一个审批是撤销的，那就保留拒绝链，如果上一个是拒绝的，才加拒绝链,
            // 拒绝链指：上个申请的审批被拒绝了,则这次的审批的refuseChain保存的就是上次的审批id
            if (request.getFinished() == Finished.REFUSED) {
                // 第一条驳回
                if (StringUtils.isEmpty(request.getRefuseChain())) {
                    request.setRefuseChain(String.valueOf(parentRequestId));
                } else {
                    request.setRefuseChain(request.getRefuseChain() + "," + parentRequestId);
                }
            }
        }
        return request;
    }

    private void isTwiceSubmitParentRequest(Request parentRequest) {
        if (parentRequest.getFinished() == Finished.CALLING_BACK
                || parentRequest.getFinished() == Finished.CANCELLED) {

            if (Objects.equals(parentRequest.getIsResubmit(), 1)) {
                throw new RpcServerLogicalException(1, "该审批已被再次编辑");
            }
        }
    }


    private String chooseConditionRequestFlow(Template template, Request request) throws Throwable {
        List<ConditionFlow> list = template.getConditionFlow();
        Map<Integer, TemplateComponent> componentMap = new HashMap<>();
        for (TemplateComponent component : request.getComponentsContent()) {
            componentMap.put(component.getNum(), component);
        }

        return ConditionFlowUtils.chooseConditionFlow(request, list, componentMap, template);
    }


    private void checkConditionRequestFlow(Template template, Request request, TemplateDao templateDao) throws Throwable {
        // 判断是否是条件审批流,然后根据相应的条件获取审批流
        String requestFlow = template.getRequestFlow();
        if (RequestFlowType.CONDITION.equals(requestFlow)) {
            List<ConditionFlow> conditionFlows = templateDao.selectConditionFlowList(template.getTemplateId());
            template.setConditionFlow(conditionFlows);
            if (template.getConditionFlow().size() == 0) {
                throw new RpcServerLogicalException(1, "模板数据错误，条件设置不存在");
            }
            requestFlow = chooseConditionRequestFlow(template, request);
            template.setRequestFlow(requestFlow);
        }
    }

    private void constructFinalRequestFlow(User user, Request request, Template template, TemplateDao templateDao, GlobalSettingDao globalSettingDao) throws Throwable {
        // 处理条件审批流
        checkConditionRequestFlow(template, request, templateDao);
        try {
            List<Flow> flows;
            if (!Objects.equals(RequestFlowType.ALTERNATIVE, template.getRequestFlow()) || Utils.isEmpty(request.getFlows())) {
                flows = FlowFactory.build(user, template.getRequestFlow(), request.getAllJudgerToEdit());
            } else {
                flows = request.getFlows();
            }
            for (Flow flow : flows) {
                flow.setCompanyId(user.getCompanyId());
                flow.setRequestId(request.getRequestDkey());
                flow.setIsApproved(0);
                flow.setIsApprovedAt(null);
            }

            GlobalSetting globalSetting = globalSettingDao.selectGlobalSetting(user.getCompanyId());
            DuplicateStrategy duplicateStrategy = new DuplicateStrategyImpl(globalSetting.getDuplicateStrategy());
            flows = duplicateStrategy.removeRepeat(flows);

            List<Flow> followFlows = FlowUtils.getFollowFlows(flows, 1);
            for (Flow flow : followFlows) {
                flow.setIsApproved(Flow.NOT_YET);
            }
            logger.info("新版审批流:{}", flows);
            request.setFlows(flows);

            request.setApplicant(user.getContactId());
            request.setApplicantCiid(user.getCompanyInfoId());

            request.setAllJudger(FlowUtils.getContactIds(flows));
            request.setAllJudgerCiid(FlowUtils.getCompanyInfoIds(flows));
            request.setRequestFlow(request.getAllJudger());
            // 进入第一层审批
            Flow flow = flows.get(0);
            if (Utils.isNotNull(flow)) {
                request.setWhoTheNext(String.valueOf(flow.getContactId()));
                request.setWhoTheNextCiid(String.valueOf(flow.getCompanyInfoId()));
            }

            if (Objects.equals(template.getIsFinancial(), 1)) {
                String financialFlow = StringUtils.isNotEmpty(template.getFinancialFlow()) ? template.getFinancialFlow() : globalSetting.getFinancialFlow();
                if (StringUtils.isEmpty(financialFlow)) {
                    throw new RpcServerLogicalException(1, "付款审批流不能为空");
                }
                List<Flow> financialFlows = FlowFactory.build(user, financialFlow, null);
                int maxOrdering = FlowUtils.getMaxOrdering(flows);
                for (Flow ff : financialFlows) {
                    // 追加到普通审批流后面
                    ff.setOrdering(++maxOrdering);
                    ff.setFlowType(Flow.FLOW_TYPE_FINANCIAL);
                    ff.setCompanyId(user.getCompanyId());
                    ff.setRequestId(request.getRequestDkey());
                    ff.setIsApproved(Flow.NOT_YET);
                    ff.setIsApprovedAt(null);
                }
                request.setFlows(ListUtils.union(flows, financialFlows));
            }
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw new RpcServerLogicalException(1, "审批流错误");
        }
    }

    private void checkBusinessTripTemplate(Request request, Template template) {
        if (template.getDefaultType() == Template.BUSINESS_TRIP) {
            List<TemplateComponent> conList = JSONArray.parseArray(template.getTemplateComponent(), TemplateComponent.class);
            List<TemplateComponent> tcList = request.getComponentsContent();
            if (tcList == null || tcList.isEmpty()) {
                throw new RpcServerLogicalException(1, "审批内容提交失败，请稍候再试");
            }
            SimpleDateFormat sdf = DateUtils.getSingleSDF(DateUtils.YMDHM);

            // 地点数目 = 目的地 + 出发地
            int countSite = 0;

            // 出差开始时间、返回时间
            Long startTime = null;
            Long endTime = null;

            for (TemplateComponent con : conList) {         // 数据库模板组件
                for (TemplateComponent tc : tcList) {       // 前端模板组件
                    if (!Objects.equals(con.getNum(), tc.getNum())) {
                        continue;
                    }

                    if (con.getNeed() == 1) {
                        if (StringUtils.isEmpty(tc.getValue())) {
                            throw new RpcServerLogicalException(1, "请填写必填项");
                        }

                        if (tc.getType() == ContentType.LOCATION && (tc.getNum() == 3 || "目的地".equals(tc.getName()))) {
                            JSONArray array = JSON.parseArray(tc.getValue());
                            countSite += array.size();                      // 目的地数目,可以有多个
                            continue;
                        }

                        if (tc.getType() == ContentType.TIME) {             // 判断是否时间类型
                            try {
                                if (con.getNum() == 6) {
                                    startTime = sdf.parse(tc.getValue()).getTime();
                                }
                                if (con.getNum() == 7) {
                                    endTime = sdf.parse(tc.getValue()).getTime();
                                }
                            } catch (Throwable e) {
                                throw new RpcServerLogicalException(1, "时间格式有误");
                            }
                        }

                    } else {
                        if (tc.getType() == ContentType.LOCATION && (tc.getNum() == 2 || "出发地".equals(tc.getName()))) {
                            if (tc.getValue() == null || "".equals(tc.getValue().trim())) {
                                throw new RpcServerLogicalException(1, "请填写出发地");
                            }
                            countSite++;          // 出发地,只能有一个
                        }
                    }
                }

            }

            String outPosition = request.getOuterPosition();
            if (StringUtils.isEmpty(outPosition)) {
                throw new RpcServerLogicalException(1, "获取定位失败，请重试");
            }

            String[] positionList = outPosition.split("#");
            if (positionList.length != countSite) {  // 地点数目 跟 经纬度的数目必须要相同
                throw new RpcServerLogicalException(1, " 获取定位失败，请重试");
            }

            for (String position : positionList) {
                if (position.split(",").length != 2) {  // 如果经纬度格式不是: xx,xx
                    throw new RpcServerLogicalException(1, "获取定位失败，请重试");
                }
            }

            // 出差开始时间、返回时间
            logger.info("出差开始时间{},结束时间{}", startTime, endTime);
            if (startTime == null || endTime == null) {
                throw new RpcServerLogicalException(1, "请填写开始时间或返回时间");
            } else {
                if (startTime > endTime) {
                    throw new RpcServerLogicalException(1, "出发时间不得晚于返回时间");
                }
                if (TimeUtils.compareWithCurrentTimeWithoutTime(startTime) == -1) {
                    throw new RpcServerLogicalException(1, "外出日期不得早于当前日期");
                }
            }
        }
    }

    private void check(Request request, User user, Template template, SubmitRequestDao submitRequestDao,
                       SelectTemplateDao selectTemplateDao, AppendSignDao appendSignDao) throws Throwable {
        // 检查权限
        checkDeptRangePermission(template, user);
        if (request.getGeneration() >= Constants.DEFAULT_GENERATION) {
            ComponentCheck.checkRequestContents(request, template, user, submitRequestDao, selectTemplateDao, appendSignDao);
        } else {
            // 兼容旧版本
            logger.info("generation:{}", request.getGeneration());

            // 加班模板
            ComponentCheck.checkWorkOverTime(template, request);

            // 报销模板
            ComponentCheck.checkExpenseTemplate(request, template);

            // 补打卡
            ComponentCheck.checkCheckInTemplate(request, template, appendSignDao, user);

            // 补假模板
            ComponentCheck.checkCompensatoryLaveTemplate(request, template, submitRequestDao, user);

            // 老版本请假、外出模板
            ComponentCheck.checkLeaveAndOutDoorTemplate(request, template, user);

            // 检查必填项
            checkNeedFilling(request, template);

            // 出差模板参数检测
            checkBusinessTripTemplate(request, template);
        }

        // 检查报销顶 : 添加新模板的报销
        checkAmountTop(request, template);

        // 检查附件
        checkAndDecodeAttach(request, template);
    }

    private void initAndCheckComponentData(Request request) {
        List<TemplateComponent> componentsContent = request.getComponentsContent();
        for (TemplateComponent component : componentsContent) {
            String value = component.getValue();
            if (StringUtils.isBlank(value)) {
                continue;
            }
            switch (component.getType()) {
                case ContentType.TEXT:
                    if (value.length() > 2000) {
                        throw new RpcServerLogicalException(1, "多行文本最多支持2000个字符");
                    }
                    break;
                case ContentType.COMPONENT_GROUP:
                    List<GroupContent> groupContents = JSONObject.parseArray(value, GroupContent.class);
                    for (GroupContent groupContent : groupContents) {
                        String valueStr = JSONObject.toJSONString(groupContent.getValue());
                        if (StringUtils.isBlank(valueStr)) {
                            continue;
                        }
                        if (valueStr.length() > 2000) {
                            throw new RpcServerLogicalException(1, "单个组件集最多支持2000个字符");
                        }
                    }
                    component.setGroupContents(groupContents);
                    if (value.length() > 4000) {
                        if (groupContents.size() > 2) {
                            component.setValue(JSONObject.toJSONString(groupContents.subList(0, 2)));
                        }
                    }
                    break;
                default:
                    if (value.length() > 4000) {
                        throw new RpcServerLogicalException(1, "组件内容过多,请删减后再提交");
                    }
            }
        }
    }


    /**
     * 处理套件
     *
     * @param template
     * @param request
     * @param user
     */
    private void dealSuite(Template template, Request request, User user) {
        try {
            switch (template.getDefaultType()) {
                case Template.LEAVE:
                case Template.COMPENSATORY_LEAVE:
                    request.setLeave(getLeave(request, user));
                    break;
                case Template.OUTDOOR:
                case Template.BUSINESS_TRIP:
                    request.setOutdoor(getOutdoor(template, request, user));
                    break;
                case Template.APPEND_SIGN:
                    request.setAppendSigns(getAppendSigns(request, user));
                    break;
                case Template.WORK_OVERTIME:
                    List<Overtime> overtimes = getOvertimes(template, request, user);
                    overtimes.sort(Comparator.comparing(Overtime::getStart));
                    request.setOvertimes(overtimes);
                    break;
                case Template.EXPENSES:
                case Template.PURCHASE:
                    request.setExpenses(getExpenses(template, request, user));
                    break;
                case Template.DAYS_OFF:
                    request.setLeave(getDaysOff(request, user));
                    break;
                default:
                    break;

            }
        } catch (Throwable throwable) {
            logger.error(throwable.getMessage(), throwable);
            if (throwable instanceof RpcServerLogicalException) {
                ExceptionMessage exceptionMessage = JSONObject.parseObject(throwable.getMessage(), ExceptionMessage.class);
                throw new RpcServerLogicalException(1, exceptionMessage.getRespDesc());
            } else {
                EmailUtils.sendMail("设置套件出错", request);
            }
        }
    }

    private List<Expense> getExpenses(Template template, Request request, User user) {
        Map<Integer, String> componentMap = ComponentUtils.getComponentValueMap(request.getComponentsContent());

        int remarkIndex = 0;
        for (TemplateComponent templateComponent : request.getComponentsContent()) {
            if ("备注".equals(templateComponent.getName())) {
                remarkIndex = templateComponent.getNum();
                break;
            }
        }
        List<Expense> expenses = new ArrayList<>();
        if (template.getDefaultType() == Template.EXPENSES) {
            List<String> amounts = JSONObject.parseArray(componentMap.get(2), String.class);
            for (int i = 0; i < amounts.size(); i++) {
                String amount = amounts.get(i);
                List<String> expenseItems = JSONObject.parseArray(componentMap.get(1), String.class);
                List<String> dates = JSONObject.parseArray(componentMap.get(3), String.class);

                Expense expense = getExpense(request, expenseItems.get(i), amount, dates.get(i), componentMap.get(4));
                expense.setCompanyId(user.getCompanyId());
                BigDecimal totalAmount;
                totalAmount = new BigDecimal(request.getTotalMoney()).setScale(6, RoundingMode.HALF_UP);
                expense.setTotalAmount(totalAmount);
                expense.setType(1);
                expense.setRemark(componentMap.get(remarkIndex));
                expenses.add(expense);
            }
        } else {
            Expense expense = getExpense(request, componentMap.get(1), componentMap.get(2), componentMap.get(3), componentMap.get(4));
            expense.setTotalAmount(expense.getPayAmount());
            expense.setCompanyId(user.getCompanyId());
            expense.setType(2);
            expense.setRemark(componentMap.get(remarkIndex));
            expenses.add(expense);
        }

        return expenses;
    }

    private Expense getExpense(Request request, String expenseItem, String amount, String payDate, String attachFile) {
        Expense expense = new Expense();
        expense.setApprovalNum(request.getApprovalNum());
        expense.setRequestId(request.getRequestDkey());
        expense.setCompanyInfoId(request.getApplicantCiid());
        expense.setCreateTime(DateTimeHelper.format(request.getCreateTimestamp(), DateTimeHelper.DEFAULT_PATTERN));
        BigDecimal payAmount = new BigDecimal(amount).setScale(6, RoundingMode.HALF_UP);
        expense.setPayAmount(payAmount);
        expense.setExpenseItem(expenseItem);
        if (StringUtils.isEmpty(payDate)) {
            payDate = DateTimeHelper.format(new Date());
            logger.info("时间为空:{}", request.getRequestDkey());
        }
        expense.setPayDate(payDate);
        expense.setAttachFile(attachFile);
        expense.setType(1);
        if (request.getFinished() < 0 || request.getFinished() > 2) {
            expense.setState(2);
        } else {
            expense.setState(1);
        }
        return expense;
    }

    private List<Overtime> getOvertimes(Template template, Request request, User user) throws Throwable {
        if (Utils.isNotEmpty(request.getOvertimes())) {
            for (Overtime overtime : request.getOvertimes()) {
                overtime.setApprovalNum(request.getApprovalNum());
                overtime.setCompanyId(user.getCompanyId());
                overtime.setCompanyInfoId(user.getCompanyInfoId());
                overtime.setRequestId(request.getRequestDkey());
            }
            return request.getOvertimes();
        }

        Overtime overtime = new Overtime();
        overtime.setCompanyId(user.getCompanyId());
        overtime.setCompanyInfoId(user.getCompanyInfoId());
        overtime.setRequestId(request.getRequestDkey());

        Map<Integer, String> componentMap = ComponentUtils.getComponentValueMap(request.getComponentsContent());
        List<TemplateComponent> templateComponents = JSONObject.parseArray(template.getTemplateComponent(), TemplateComponent.class);

        overtime.setStartTime(componentMap.get(1));
        overtime.setEndTime(componentMap.get(2));
        overtime.setShowTime(componentMap.get(3));

        for (TemplateComponent component : templateComponents) {
            switch (component.getName()) {
                case "加班原因":
                    overtime.setReason(componentMap.get(component.getNum()));
                    break;
                case "加班地点":
                    overtime.setAddress(componentMap.get(component.getNum()));
                    overtime.setCoordinate(request.getOuterPosition());
                    break;
                default:
                    break;
            }
        }
        Date start = DateTimeHelper.parse(overtime.getStartTime(), DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN);
        Date end = DateTimeHelper.parse(overtime.getEndTime(), DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN);
        CheckInDataServicePrx checkInDataServicePrx = ServantUtils.getObject(CheckInDataServicePrx.class);
        TimeDetail param = new TimeDetail();
        param.setCompanyId(user.getCompanyId());
        param.setCompanyInfoId(user.getCompanyInfoId());
        param.setStart(start);
        param.setEnd(end);
        List<TimeDetail> timeDetails = checkInDataServicePrx.calculateOvertimeDetail(param);
        List<Overtime> newOvertimes = new ArrayList<>();
        for (TimeDetail detail : timeDetails) {
            Overtime overtimeCopy = com.plusmoney.util.BeanUtils.copyProperties(Overtime.class, overtime);
            overtimeCopy.setStart(detail.getStart());
            overtimeCopy.setEnd(detail.getEnd());
            overtimeCopy.setDuration((int) ((detail.getEnd().getTime() - detail.getStart().getTime()) / 1000));
            overtimeCopy.setShowTime(DateTimeUtils.getTimeDuration(detail.getStart(), detail.getEnd()));
            newOvertimes.add(overtimeCopy);
        }
        return newOvertimes;
    }

    private List<AppendSign> getAppendSigns(Request request, User user) {
        List<AppendSign> appendSigns = new ArrayList<>();

        Map<Integer, String> componentMap = ComponentUtils.getComponentValueMap(request.getComponentsContent());

        appendSigns.add(getAppendSign(request, user, componentMap, 1));

        if (componentMap.containsKey(6)) {
            appendSigns.add(getAppendSign(request, user, componentMap, 6));
        }

        return appendSigns;
    }

    private AppendSign getAppendSign(Request request, User user, Map<Integer, String> componentMap, int beginIndex) {
        String reason = componentMap.get(10);
        String time = componentMap.get(beginIndex);
        JSONObject signType = JSONObject.parseObject(componentMap.get(beginIndex + 1));
        JSONObject schedule = JSONObject.parseObject(componentMap.get(beginIndex + 2));
        JSONObject workTime = JSONObject.parseObject(componentMap.get(beginIndex + 3));

        AppendSign appendSign = new AppendSign();
        appendSign.setApprovalNum(request.getApprovalNum());
        appendSign.setCompanyId(user.getCompanyId());
        appendSign.setCompanyInfoId(user.getCompanyInfoId());
        appendSign.setRequestId(request.getRequestDkey());
        appendSign.setReason(reason);
        if (Utils.isNotNull(signType)) {
            appendSign.setSignType(signType.getInteger("signType"));
        }
        appendSign.setSignTime(DateTimeHelper.parse(time, DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN));
        appendSign.setDate(DateTimeHelper.format(appendSign.getSignTime()));
        if (Utils.isNotNull(schedule)) {
            appendSign.setScheduleName(schedule.getString("scheduleName"));
        }
        if (Utils.isNotNull(workTime)) {
            appendSign.setWorkTimeRange(workTime.getString("scheduleWorkTime"));
            Integer workTimeId = workTime.getInteger("scheduleWorkTimeId");
            if (Objects.equals(workTimeId, -3)) {
                logger.info("三个打卡时段");
                appendSign.setSignType(appendSign.getSignType() + 4);
            } else if (Objects.equals(workTimeId, -2)) {
                logger.info("两个打卡时段");
                appendSign.setSignType(appendSign.getSignType() + 2);
            }
        }
        return appendSign;
    }

    private Outdoor getOutdoor(Template template, Request request, User user) {
        Outdoor outdoor = new Outdoor();
        outdoor.setApprovalNum(request.getApprovalNum());
        if (Utils.isNotNull(request.getOutdoor())) {
            // 暂时只用这个字段
            outdoor.setAttachFile(request.getOutdoor().getAttachFile());
        }
        outdoor.setCompanyId(user.getCompanyId());
        outdoor.setCompanyInfoId(user.getCompanyInfoId());
        outdoor.setRequestId(request.getRequestDkey());
        if (StringUtils.isNotBlank(template.getOutSignOffset())) {
            outdoor.setSignOffset(Integer.parseInt(template.getOutSignOffset()));
        } else {
            outdoor.setSignOffset(1000);
        }

        Map<Integer, String> componentMap = ComponentUtils.getComponentValueMap(request.getComponentsContent());
        List<TemplateComponent> templateComponents = JSONObject.parseArray(template.getTemplateComponent(), TemplateComponent.class);

        if (request.getDefaultType() == Template.OUTDOOR) {
            outdoor.setType(1);
            outdoor.setStartTime(componentMap.get(1));
            outdoor.setEndTime(componentMap.get(2));
            outdoor.setDestAddress(componentMap.get(3));
            for (TemplateComponent templateComponent : templateComponents) {
                String value = componentMap.get(templateComponent.getNum());
                if ("外出原因".equals(templateComponent.getName())) {
                    outdoor.setReason(value);
                    break;
                }
            }
            if (StringUtils.isNotBlank(request.getOuterPosition())) {
                outdoor.setDestCoordinate(request.getOuterPosition());
            }
        } else {
            outdoor.setType(2);
            outdoor.setReason(componentMap.get(1));
            String srcAddress = componentMap.get(2);
            if (StringUtils.isNotEmpty(srcAddress) && srcAddress.contains(";")) {
                // 高德地图地位会把公交车站所有线路都加上,故处理一下
                /* e.g.
                    广东省深圳市罗湖区(停运)大鹏新区假日专线2路;(停运)旅游8号线;103路;111路;205路;205路越站车;
                    308路;308路越站2;381路;387路;57路;69路;85路;85路越站车;M133路;M191路;M207路;M348路;M364路;
                    M437路;M437路越站1;M555路;M555路区间车;N14路;高峰专线58路;高峰专线58路大站快车
                 */
                srcAddress = srcAddress.split(";")[0];
            }
            outdoor.setSrcAddress(srcAddress);
            outdoor.setDestAddress(componentMap.get(3));
            if (StringUtils.isNotBlank(request.getOuterPosition())) {
                LinkedList<String> positions = new LinkedList<>(Arrays.asList(request.getOuterPosition().split("#")));
                if (StringUtils.isNotBlank(outdoor.getSrcAddress())) {
                    // 有设置出发地点,则第一个为出发地点经纬度
                    outdoor.setSrcCoordinate(positions.getFirst());
                    positions.removeFirst();
                }
                if (Utils.isNotEmpty(positions)) {
                    StringBuilder sb = new StringBuilder();
                    for (String position : positions) {
                        sb.append(position).append("#");
                    }
                    outdoor.setDestCoordinate(sb.toString().substring(0, sb.length() - 1));
                }
            }
            outdoor.setStartTime(componentMap.get(6));
            outdoor.setEndTime(componentMap.get(7));

            for (TemplateComponent templateComponent : templateComponents) {
                String value = componentMap.get(templateComponent.getNum());
                if ("交通方式".equals(templateComponent.getName())) {
                    outdoor.setTransportation(value);
                }
                if (templateComponent.getName().contains("住宿")) {
                    if (StringUtils.isNotBlank(value)) {
                        outdoor.setStayDay(value);
                    }
                }
            }

        }
        outdoor.setDuration(OutdoorUtils.calculateDuration(outdoor));
        return outdoor;
    }

    private Leave getDaysOff(Request request, User user) throws Throwable {
        if (Utils.isNotNull(request.getLeave())) {
            return request.getLeave();
        }
        Leave leave = new Leave();
        leave.setApprovalNum(request.getApprovalNum());
        leave.setCompanyId(user.getCompanyId());
        leave.setCompanyInfoId(user.getCompanyInfoId());
        leave.setRequestId(request.getRequestDkey());

        Map<Integer, String> componentMap = ComponentUtils.getComponentValueMap(request.getComponentsContent());
        String startTime = componentMap.get(2);
        String endTime = componentMap.get(3);

        leave.setLeaveType("调休");
        leave.setReason(componentMap.get(5));

        LeaveInfo leaveInfo;
        try {
            leaveInfo = JSONObject.parseObject(componentMap.get(4), LeaveInfo.class);
            leave.setLeaveTimeDetail(componentMap.get(4));
        } catch (Throwable e) {
            logger.error("json解析错误", e);
            CheckInDataServicePrx checkInDataServicePrx = ServantUtils.getObject(CheckInDataServicePrx.class);
            LeaveInfo param = new LeaveInfo();
            param.setLeaveStartTime(DateTimeHelper.format(leave.getRealStartTime(), DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN));
            param.setLeaveEndTime(DateTimeHelper.format(leave.getRealEndTime(), DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN));
            param.setCompanyId(user.getCompanyId());
            param.setContactId(user.getContactId());
            param.setLeaveCalculateType(leave.getLeaveCalculateType());
            param.setLeaveTimeUnit(leave.getLeaveTimeUnit());
            leaveInfo = checkInDataServicePrx.calculateLeaveTime(param);
            leave.setLeaveTimeDetail(JSONObject.toJSONString(leaveInfo));
        }
        if (Utils.isNull(leaveInfo)) {
            throw new RpcServerLogicalException("1", "获取请假信息失败");
        }
        leave.setDuration(leaveInfo.getLeaveTimestamp());

        leave.setShowTime(leaveInfo.getShowTime());
        leave.setRealStartTime(DateTimeHelper.parse(startTime, DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN));
        leave.setRealEndTime(DateTimeHelper.parse(endTime, DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN));
        leave.setBalanceType(Leave.BALANCE_TYPE_DAYS_OFF);
        return leave;
    }

    private Leave getLeave(Request request, User user) throws Throwable {
        if (Utils.isNotNull(request.getLeave())) {
            return initLeave(request, user);
        }
        Leave leave = new Leave();
        leave.setApprovalNum(request.getApprovalNum());
        leave.setCompanyId(user.getCompanyId());
        leave.setCompanyInfoId(user.getCompanyInfoId());
        leave.setRequestId(request.getRequestDkey());

        Map<Integer, String> componentMap = ComponentUtils.getComponentValueMap(request.getComponentsContent());
        String startTime = componentMap.get(1);
        String endTime = componentMap.get(2);

        leave.setLeaveType(componentMap.get(3));
        leave.setReason(componentMap.get(4));
        leave.setDuration(Integer.parseInt(request.getRequestContentLastTotalSecond()));
        leave.setShowTime(request.getNaturalContentTimeLast());
        leave.setRealStartTime(DateTimeHelper.parse(startTime, DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN));
        leave.setRealEndTime(DateTimeHelper.parse(endTime, DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN));

        LeaveInfo leaveInfo;
        if (StringUtils.isEmpty(request.getLeaveDuration())) {
            CheckInDataServicePrx checkInDataServicePrx = ServantUtils.getObject(CheckInDataServicePrx.class);
            LeaveInfo param = new LeaveInfo();
            param.setLeaveStartTime(DateTimeHelper.format(leave.getRealStartTime(), DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN));
            param.setLeaveEndTime(DateTimeHelper.format(leave.getRealEndTime(), DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN));
            param.setRealStartTime(leave.getRealStartTime());
            param.setRealEndTime(leave.getRealEndTime());
            param.setCompanyId(user.getCompanyId());
            param.setContactId(user.getContactId());
            param.setLeaveCalculateType(leave.getLeaveCalculateType());
            param.setLeaveTimeUnit(leave.getLeaveTimeUnit());
            leaveInfo = checkInDataServicePrx.calculateLeaveTime(param);
        } else {
            leaveInfo = JSONObject.parseObject(request.getLeaveDuration(), LeaveInfo.class);
        }
        SimplePropertyPreFilter filter = new SimplePropertyPreFilter(LeaveInfo.class, "timeInfos", "showTime", "totalTime");
        String leaveTimeDetail = JSONObject.toJSONString(leaveInfo, filter);
        leave.setLeaveTimeDetail(leaveTimeDetail);

        if ("年假".equals(leave.getLeaveType())) {
            leave.setBalanceType(Leave.BALANCE_TYPE_ANNUAL);
        } else {
            leave.setBalanceType(Leave.BALANCE_TYPE_DEFAULT);
        }
        return leave;
    }

    /**
     * 判断是否在一个页面内重复提交了,重复提交则抛提交成功
     *
     * @param request
     * @param submitRequestDao
     * @throws Throwable
     */
    private void isSubmitAgain(Request request, User user, SubmitRequestDao submitRequestDao) throws Throwable {
        if (StringUtils.isNotEmpty(request.getContactIdSubmitTime())) {
            boolean isSubmitAgain = submitRequestDao.isSubmitAgain(user.getCompanyId(), request.getContactIdSubmitTime());

            if (isSubmitAgain) {
                throw new SpecialLogicException("002", "提交成功");
            }
        }
    }

    private Leave initLeave(Request request, User user) {
        Leave leave = request.getLeave();
        // TODO 根据人事来扣除
        if ("年假".equals(leave.getLeaveType())) {
            leave.setBalanceType(Leave.BALANCE_TYPE_ANNUAL);
        } else {
            leave.setBalanceType(Leave.BALANCE_TYPE_DEFAULT);
        }
        leave.setApprovalNum(request.getApprovalNum());
        leave.setRequestId(request.getRequestDkey());
        leave.setCompanyId(user.getCompanyId());
        leave.setCompanyInfoId(user.getCompanyInfoId());
        LeaveInfo leaveInfo = JSONObject.parseObject(leave.getLeaveTimeDetail(), LeaveInfo.class);
        SimplePropertyPreFilter filter = new SimplePropertyPreFilter(LeaveInfo.class, "timeInfos", "showTime", "totalTime");
        String leaveTimeDetail = JSONObject.toJSONString(leaveInfo, filter);
        leave.setLeaveTimeDetail(leaveTimeDetail);
        return request.getLeave();
    }

    /**
     * 提交后的处理：1.提交审批记录;  2.判断第一个审批人是否是自己,是则直接同意; 3.推送给下一个审批人;
     */
    private void afterSubmitHandle(Template template, Request request, User user) throws Throwable {
        UserServicePrx userServicePrx = ServantUtils.getObject(UserServicePrx.class);

        List<Flow> firstFlows = FlowUtils.getFlowsByOrdering(request.getFlows(), 1);
        if (FlowUtils.isExistUserFlow(firstFlows, user)) {
            logger.info("审批流第一个人是自己");
            request.setRequestOperation(Flow.CONFIRM);

            ApprovalOperateServicePrx approvalOperateService = ServantUtils.getObject(ApprovalOperateServicePrx.class);
            approvalOperateService.confirmRequest(request.getRequestDkey(), user);
        } else {
            // 多身份推送 否则给第一个审批人推送
            Set<Integer> userIds = new HashSet<>();
            Set<Integer> contactIds = new HashSet<>();
            for (Flow flow : firstFlows) {
                List<User> users = userServicePrx.getMultiUserListByCompanyInfoId(flow.getCompanyId(), flow.getCompanyInfoId());
                for (User u : users) {
                    userIds.add(u.getUserId());
                    contactIds.add(u.getContactId());
                }
            }
            String objectJson = request.getRequestDkey() + "," + request.getTemplateId() + "," + request.getVersion();
            RocketUtils.pushManyToOneMsg(userIds, request.getRequestDkey(), "您有一条新审批待处理", objectJson);

            RocketUtils.pushToRefresh(user, contactIds);
        }

        if (Utils.isNotEmpty(request.getParticipants())) {
            List<Integer> contactIds = request.getParticipants().stream().map(Participant::getContactId).collect(Collectors.toList());
            RocketUtils.pushToRefresh(user, contactIds);
        }

        // 开通权限（'我处理的'栏目）
        RocketUtils.sendAddAuthMsg(request.getAllJudger(), user.getCompanyId());
        // 重结算人事调休时长 TODO(暂时保留调休模板,后期去掉)
        if (template.getDefaultType() == Template.DAYS_OFF) {
            RocketUtils.sendHrCalculateDaysOff(user.getCompanyId(), user.getCompanyInfoId());
        }

        if (Utils.isNotNull(request.getLeave())) {
            RocketUtils.sendLeaveTypeMessage(request);
        }

        //提交审批工作动态
        if (ApprovalUtils.isTestWorkTrend(user.getCompanyId())) {
            sendWorkTrendMsg(template, request, user);
        }

        // 绑定附件
        bindAttachment(request, user);

        // 加缓存,防重复提交
        Jedis jedis = RedisUtils.getJedis();
        jedis.setex(request.getContactIdSubmitTime(), 3600, "");
        RedisUtils.close(jedis);
    }

    private void sendWorkTrendMsg(Template template, Request request, User user) throws Throwable {
        String title = "发起" + template.getTemplateName() + "审批";
        String content;
        request.setCreateTimestamp(new Date());

        switch (template.getDefaultType()) {
            case Template.LEAVE:
            case Template.COMPENSATORY_LEAVE:
            case Template.DAYS_OFF:
                content = request.getLeave().getReason();
                break;
            case Template.OUTDOOR:
            case Template.BUSINESS_TRIP:
                content = request.getOutdoor().getReason();
                break;
            case Template.APPEND_SIGN:
                content = request.getAppendSigns().get(0).getReason();
                break;
            case Template.WORK_OVERTIME:
                content = request.getOvertimes().get(0).getReason();
                break;
            case Template.EXPENSES:
            case Template.PURCHASE:
                Expense expense = request.getExpenses().get(0);
                String expenseItem = expense.getExpenseItem();
                if (request.getExpenses().size() > 1) {
                    expenseItem += "等";
                }
                content = expenseItem;
                break;
            default:
                Summary otherSignSummary = SummaryUtils.getOtherSignSummary(request);
                content = otherSignSummary.getContent();
                break;
        }

        RocketUtils.sendApprovalWorkTrendsMsg(request, user, title, content);
    }

    private void bindAttachment(Request request, User user) {
        try {
            List<String> fileNames = getFileNames(request);
            if (Utils.isEmpty(fileNames)) {
                return;
            }
            AttachmentDTO attachmentDTO = new AttachmentDTO();
            attachmentDTO.setCompanyId(user.getCompanyId());
            attachmentDTO.setObjectId(String.valueOf(request.getRequestDkey()));
            attachmentDTO.setFileNames(fileNames);
            AttachmentServicePrx attachmentService = ServantUtils.getObject(AttachmentServicePrx.class);
            attachmentService.updateAttachmentObjectId(attachmentDTO);
            logger.info("提交成功,清除组件信息");
        } catch (Throwable throwable) {
            logger.error(throwable.getMessage(), throwable);
            EmailUtils.sendMail("上传附件失败", throwable);
        }
    }

    private List<String> getFileNames(Request request) {
        List<String> list = new ArrayList<>();
        List<TemplateComponent> attachment = ComponentUtils.getComponentByType(request.getComponentsContent(), ContentType.ATTACHMENT);
        if (Utils.isNotEmpty(attachment)) {
            for (TemplateComponent templateComponent : attachment) {
                List<String> fileNames = JSONObject.parseArray(templateComponent.getValue(), String.class);
                if (Utils.isEmpty(fileNames)) {
                    continue;
                }
                list.addAll(fileNames);
            }
        }

        try {
            List<TemplateComponent> componentGroups = ComponentUtils.getComponentByType(request.getComponentsContent(), ContentType.COMPONENT_GROUP);
            for (TemplateComponent componentGroup : componentGroups) {
                List<GroupContent> groupContents = componentGroup.getGroupContents();
                if (Utils.isEmpty(groupContents)) {
                    continue;
                }
                for (GroupContent groupContent : groupContents) {
                    List<Component> components = groupContent.getValue();
                    if (Utils.isEmpty(components)) {
                        continue;
                    }
                    for (Component component : components) {
                        if (StringUtils.isBlank(component.getValue())) {
                            continue;
                        }
                        if (Objects.equals(component.getType(), ContentType.ATTACHMENT)) {
                            List<String> fileNames = JSONObject.parseArray(component.getValue(), String.class);
                            list.addAll(fileNames);
                        }
                    }
                }
            }
        } catch (Exception e) {
            // 2020年1月9日 21:25:10
            logger.error(e.getMessage(), e);
        }

        Outdoor outdoor = request.getOutdoor();
        if (Utils.isNotNull(outdoor) && StringUtils.isNotBlank(outdoor.getAttachFile())) {
            List<String> fileNames = ListHelper.string2StringList(outdoor.getAttachFile());
            list.addAll(fileNames);
        }

        return list;
    }

    private String getContentNumList(List<TemplateComponent> components) {
        List<Integer> contentNumList = new ArrayList<>(components.size());

        for (TemplateComponent component : components) {
            contentNumList.add(component.getNum());
        }

        return ListHelper.list2string(contentNumList);
    }

    private String getContentIds(List<TemplateComponent> components) {
        List<Integer> contentIds = new ArrayList<>(components.size());

        for (TemplateComponent component : components) {
            contentIds.add(component.getDbkey());
        }

        return ListHelper.list2string(contentIds);
    }


    private void submitDataToDataBase(User user, Request request, RequestDao requestDao, SubmitRequestDao submitRequestDao,
                                      CcDao ccDao, ParticipantDao participantDao, LeaveDao leaveDao, OutdoorDao outdoorDao, AppendSignDao appendSignDao,
                                      OvertimeDao overtimeDao, ExpenseDao expenseDao, MoneyDao moneyDao, FlowDao flowDao, ComponentDao componentDao) throws Throwable {
        // 处理拒绝链
        if (Utils.isNotNull(request.getParentRequestId()) && request.getParentRequestId() != 0) {
            request = dealWithRequestChain(request, user, submitRequestDao);
        }

        // 保存审批流
        requestDao.insertRequestInfo(request);
        // 保存组件信息
        componentDao.insertBatchComponent(request.getComponentsContent());

        List<TemplateComponent> groupComponents = ComponentUtils.getComponentByType(request.getComponentsContent(), ContentType.COMPONENT_GROUP);
        if (Utils.isNotEmpty(groupComponents)) {
            componentDao.insertBatchGroupComponent(groupComponents);
        }

        // 插入套件信息
        if (Utils.isNotNull(request.getLeave())) {
            leaveDao.insertLeave(request.getLeave());
        }
        if (Utils.isNotNull(request.getOutdoor())) {
            outdoorDao.insertOutdoor(request.getOutdoor());
        }
        if (Utils.isNotEmpty(request.getAppendSigns())) {
            appendSignDao.insertBatchAppendSigns(request.getAppendSigns());
        }
        if (Utils.isNotEmpty(request.getOvertimes())) {
            overtimeDao.insertBatchOvertimes(request.getOvertimes());
        }
        if (Utils.isNotEmpty(request.getExpenses())) {
            expenseDao.insertBatchExpenses(request.getExpenses());
        }
        try {
            if (Utils.isNotEmpty(request.getMoneys())) {
                moneyDao.insertMoneys(request.getMoneys());
            }
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            EmailUtils.sendMail("保存金额信息失败", e);
        }


        // 插入审批流
        if (Utils.isNotEmpty(request.getFlows())) {
            flowDao.insertFlows(request.getFlows());
        }

        // 插入参与人
        if (Utils.isNotEmpty(request.getParticipants())) {
            participantDao.insertBatchParticipants(request.getParticipants());
        }

        // 插入抄送人
        if (Utils.isNotEmpty(request.getCcList())) {
            ccDao.insertBatchCc(request.getCcList());
        }
    }

    @Override
    public List<Integer> getApprovalCc(User user, Query query) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        TemplateDao templateDao = new TemplateDaoImpl(connection);
        Template template = templateDao.selectTemplateDetailById(query.getTemplateId());
        if (StringUtils.isBlank(template.getCc())){
            return new ArrayList<>();
        }
        logger.info("模版默认抄送人:{}", template.getCc());
        List<String> templateCcList = ListHelper.string2StringList(template.getCc());
        if (templateCcList.contains("SP") || templateCcList.contains("DM") || templateCcList.contains("DSP") || templateCcList.contains("DDM")) {
            List<String> ids = new ArrayList<>();
            Map<String, List<User>> userSuperiorMap = CcUtils.get4UserSuperior(user);
            logger.info("userSuperiorMap:{}",JSONObject.toJSONString(userSuperiorMap));
            for (String key : templateCcList) {
                if (ValidateUtil.isPositiveInteger(key)) {
                    ids.add(key);
                    continue;
                }
                List<String> list = findIdsByKey(userSuperiorMap, key);
                if (Utils.isNotEmpty(list)) {
                    ids.addAll(list);
                }
            }
            return ids.stream().map(Integer::parseInt).collect(Collectors.toList());
        }
        return templateCcList.stream().map(Integer::parseInt).collect(Collectors.toList());
    }

    @Override
    public void saveExportEmail(Email email, User user) throws Throwable {
        Connection approvalConn = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            SubmitRequestDao submitDao = new SubmitRequestDaoImpl(approvalConn);
            Email existEmail = submitDao.getExportEmail(user);
            if (Utils.isNotNull(existEmail)) {
                if (Objects.equals(existEmail.getEmail(), email.getEmail())) {
                    return;
                }
                email.setId(existEmail.getId());
            }
            submitDao.saveExportEmail(email, user);
        } catch (Throwable e) {
            throw ErrorUtils.throwError(e, logger);
        } finally {
            DBUtils.release(approvalConn);
        }
    }

    @Override
    public Email getExportEmail(User user) throws Throwable {
        Connection approvalConn = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            SubmitRequestDao submitDao = new SubmitRequestDaoImpl(approvalConn);
            return submitDao.getExportEmail(user);
        } catch (Throwable e) {
            throw ErrorUtils.throwError(e, logger);
        } finally {
            DBUtils.release(approvalConn);
        }
    }

}
